Skip to content

feat: session identity, step-up approval, and OpenClaw-focused docs#13

Merged
tick10 merged 14 commits into
mainfrom
feature/v1-session-identity
Mar 20, 2026
Merged

feat: session identity, step-up approval, and OpenClaw-focused docs#13
tick10 merged 14 commits into
mainfrom
feature/v1-session-identity

Conversation

@tick10

@tick10 tick10 commented Mar 20, 2026

Copy link
Copy Markdown
Contributor

Summary

  • add session identity with role-based, short-lived sessions and approval flow
  • add step-up approval and related session lifecycle/auth updates
  • polish docs/README with OpenClaw positioning and current deployment guidance

Notes

  • release version in branch is 0.13.3
  • tag v0.13.3 has been pushed
  • follow-up work for deeper OpenClaw/Docker integration remains separate

Aaron.ClaudeContainer and others added 14 commits March 14, 2026 02:52
…ion auth

Add the v1 session identity system foundation:

- Role config schema (SessionConfig, RoleConfig) with namespace scope,
  allowed actions, bootstrap trust methods, and seal key requirement
- New internal/session package with HMAC-SHA256 signed tokens (phxs_ prefix),
  in-memory session store with background cleanup
- POST /v1/session/mint endpoint with structured denial codes
  (ROLE_NOT_FOUND, BOOTSTRAP_FAILED, SCOPE_EXCEEDED, etc.)
- Session token auth path in authenticateInfo() between short-lived
  tokens and bearer fallback
- Namespace scope enforcement in all secret handlers
- Action enforcement per session role (default: list + read_value)
- Seal key fingerprint binding at mint time, validated on requests
- Session-authenticated requests bypass ACL seal key check (use
  session-bound fingerprint instead)
- Loopback IP detection for "local" bootstrap trust method
- Forward-compatible SessionID/SessionRole in audit Entry and
  policy RequestContext
- 21 unit tests across session package, all existing tests pass
- Version bump to 0.12.0

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…tructured denials

Server:
- Session renewal endpoint (POST /v1/session/renew) with re-validation of
  role config, bootstrap trust, and attestation requirements
- Auth model: session tokens checked before mTLS so renewal requests can
  carry both session identity and current mTLS attestation evidence
- Role attestation enforcement at mint and renew (require_mtls, source_ip,
  cert_fingerprint)
- ACTION_DENIED code (was SCOPE_EXCEEDED) for session action denials
- Cache-Control: no-store on mint and renew responses
- "token" added to valid bootstrap methods
- Action name validation against known set in config
- Session-aware audit logging (LogSessionAllowed/LogSessionDenied) with
  session ID propagation across all handler audit call sites

CLI:
- Auto-mint via PHOENIX_ROLE env var (takes precedence over PHOENIX_TOKEN)
- Session token caching with per-role keys
- Seal key auto-generation and persistence (~/.phoenix/session-seal-<role>.key)
- Transparent renewal when nearing expiry (10 min buffer)
- Structured denial parsing in handleError() with code/detail/hint format

MCP:
- Background session renewal goroutine (5 min interval)
- Structured denial formatting in all tool handlers

SDK:
- NewWithRole() for fail-closed session minting
- MintSession() and RenewSession() methods
- Auto-renewal in doRequest() (5 min buffer)
- Error type extended with Code and Remediation fields

Phase 1 non-blockers fixed:
- "token" in validBootstrapMethods
- Audit SessionID populated via session-aware methods
- Cache-Control: no-store on mint response
- Role Actions validated against known names
- ACTION_DENIED replaces SCOPE_EXCEEDED for action denials

Tests: 14 new tests across session, config, and API packages.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When a role has step_up: true, session mint returns 202 with a pending
approval instead of a token. A human approves via `phoenix approve <id>`,
and the agent polls for status to receive the session token once approved.

New package internal/approval/ with in-memory store, cleanup goroutine,
and full lifecycle (pending/approved/denied/expired).

API: 4 new endpoints (/v1/approval/{id}, /v1/approval/{id}/approve,
/v1/approval/{id}/deny, /v1/approvals), step-up branch in session mint.

Security (review fixes):
- All approval endpoints require ACL admin authorization
- Status endpoint restricted to requester or admin; token only visible
  to authorized callers
- Approval-time minting re-validates full current role config:
  bootstrap trust, attestation, seal key, namespaces, actions, TTL
- Cert fingerprint stored on approval for attestation re-check

CLI: `phoenix approve` with interactive /dev/tty confirmation,
`pollForApproval` with 2s polling, TTY detection, same-TTY warning.

17 API tests + 11 approval store tests covering happy paths, denial,
expiry, admin-auth enforcement, non-admin rejection, stale role
re-checks (removed role, tightened bootstrap/attestation/seal key).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
API: GET /v1/sessions (list), GET /v1/sessions/{id} (info),
POST /v1/sessions/{id}/revoke. Session-token callers are strictly
scoped to their own session ID — no ACL escalation. Bearer/mTLS
callers see own-agent sessions; admins see/revoke all.

CLI: phoenix sessions list|info|revoke subcommands with table output,
optional --role/--agent filters.

MCP: HTTP 202 (APPROVAL_REQUIRED) handling in resolve/get/list tools
with agent-friendly remediation messages. New phoenix_session_list
and phoenix_session_revoke tools always advertised.

SDK: ListSessions(), RevokeSession(), SessionInfo struct, and error
classification helpers (IsApprovalRequired, IsSessionExpired,
IsScopeExceeded, IsActionDenied).

10 new API tests including sibling-session isolation, 3 SDK tests,
MCP tool count tests updated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… isolation

Audit: structured denial codes (SESSION_EXPIRED, SESSION_REVOKED,
SESSION_INVALID) on all auth failures instead of generic 401.
ParseClaimsInsecure extracts agent/sessionID from expired tokens
for audit logging. session.auth audit event with session_id context.
Fix revoke audit to use session-aware helper.

Security: session tokens now blocked on all admin/control-plane
endpoints (authenticate() rejects UsedSession with ADMIN_AUTH_REQUIRED).
Scoped session credentials cannot reach audit, agent, cert, status,
policy, rotate-master, or token-mint endpoints even if the underlying
agent has admin ACL. handleAuthError surfaces structured denial codes
on all auth failure paths.

SDK: add IsSessionRevoked() error helper.

Docs: new session-identity.md guide covering roles, lifecycle, CLI,
SDK, MCP, audit events, and denial codes. Updated api-reference-index,
authentication, cli-usage, configuration docs. Release notes updated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Session cache key now includes server URL hash, preventing collisions
  when the same role name is used across different Phoenix servers
- Guard global token variable with sync.RWMutex for MCP background
  renewal goroutine safety
- SDK TestNewDefaults uses t.Setenv to clear PHOENIX_SERVER/TOKEN,
  making the test hermetic regardless of shell environment

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add browser-based operator dashboard at /dashboard/ for approvals,
sessions, audit, and role inspection. All assets embedded via go:embed.

Dashboard:
- Cookie auth (bcrypt password or PIN), HMAC-signed sessions, CSRF
- Login rate limiting with exponential backoff per source IP
- Secure cookie flag auto-detected from TLS/X-Forwarded-Proto
- Approve/deny step-up requests with shared ValidateForMint checks
- Session revoke, audit log with auto-refresh, role inspection
- Dark industrial theme, mobile-responsive layout
- Audit actions logged as dashboard@<client_ip> for forensic distinction

Shared approval validation:
- Extract ValidateForMint from api.go into approval package
- Typed ValidationError with machine-readable codes
- BootstrapAllowed and CheckAttestation as standalone functions
- Both API handler and dashboard call the same safety checks

Docs:
- New docs/dashboard.md — full deployment guide, security model,
  deployment checklist, transport security requirements
- Rewrite docs/threat-model.md — dashboard attack surface, session
  identity threats, threat/mitigation/residual-risk table
- Update getting-started, authentication, api-reference-index,
  lan-deployment, session-identity, configuration docs

Tests: 10 dashboard, 7 approval validation, 7 config validation (new)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Logout now requires valid cookie + CSRF (was unprotected POST)
- Audit events added: dashboard.logout, dashboard.auth (expired cookie),
  dashboard.csrf (token mismatch)
- Docs: audit trail table updated with all event types, threat model
  updated with full audit coverage, release notes expanded
- 7 new tests for audit behavior and logout auth
- ExampleConfig includes session + dashboard sections

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…ogin (v0.12.9)

Password storage:
- Config field changed from plaintext `password` to `password_hash`
  (bcrypt hash). Plaintext rejected by validation.
- Added `phoenix-server --hash-password` to generate hashes interactively.
- Dashboard stores hash directly; no hashing at startup.

Single active session:
- Default: only one dashboard session at a time. Second login rejected
  with "another session is active" message.
- Force Login button appears on blocked login page — re-authenticates
  and invalidates the prior session via nonce rotation.
- Logout and cookie expiry clear the active nonce so new logins proceed.
- `allow_multi_login: true` config option disables enforcement.

Non-blocker fix:
- password_hash validation checks bcrypt prefix AND minimum length (59
  chars), catching truncated hashes.

Tests: 5 new (single-session block, force takeover, logout-then-login,
multi-login mode, force-login bad credential). 22 total dashboard tests.

Docs: dashboard.md, configuration.md, threat-model.md, getting-started.md,
lan-deployment.md updated for password_hash, single-session, force login.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Code changes for v0.13.3 release readiness:

- Add DELETE /v1/agents/{name} endpoint and `phoenix agent delete` CLI
  command. Backend (ACL.RemoveAgent) already existed but was never wired
  to API or CLI. Includes self-delete protection and audit logging.
- Add `phoenix-server --reissue-cert --san <ip|host>` to re-issue the
  server TLS certificate with custom SANs for LAN deployment. Uses
  existing CA, always includes localhost/127.0.0.1.
- Fix staticcheck S1000 (for-select single case) in approval poll loop
- Fix staticcheck S1011 (append loop) in cert reissue
- Fix gofmt alignment in DashboardConfig and Client structs
- Fix TypeScript SDK test env leak (PHOENIX_SERVER bleed-through)
- Bump version to 0.13.3

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Comprehensive doc review and truth pass across all public documentation:

- Fix broken first-run: README and getting-started used https:// but
  default init starts plain HTTP on loopback. All quick-start examples
  now use http://127.0.0.1:9090 with clear notes on when/how to enable TLS.
- Fix LAN deployment: added missing auth.mtls.enabled step, replaced
  nonexistent --san flag with real --reissue-cert workflow, added full
  executable walkthrough.
- Rewrite roadmap to reflect shipped state (dashboard, HTTP MCP, session
  identity were listed as planned/in-progress but are shipped).
- Add Session Identity and Dashboard rows to README docs table.
- Expand cli-usage.md with missing commands: agent create/delete, cert
  issue/revoke, keypair generate, status, verify, emergency, rotate-master,
  policy show/test.
- Add Go SDK section to integrations.md; mark Python/TS packages as not
  yet published on PyPI/npm.
- Add transport security section to configuration.md explaining the
  HTTP-by-default story.
- Document in-memory session/approval state and restart behavior.
- Document role scope vs ACL intersection semantics.
- Fix admin-token-lifecycle.md stale https:// and CA_CERT references.
- Fix getting-started.md sample startup output (showed mTLS enabled on
  default config).
- Fix config.example.json: listen address to 127.0.0.1, paths to
  /data/phoenix/, added dashboard password_hash placeholder.
- Relabel reference-only-enforcement-design.md from WIP to RFC.
- Update RELEASE_NOTES.md: add version header, add HTTP MCP transport.
- Update api-reference-index.md with agent delete endpoint.
- Remove Docker "(planned)" label — images are published.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Rework README opener with origin story and agent framework context.
Consolidate OpenClaw integration docs with containerized deployment
guide and honest limitations note. Fix localhost URL inconsistencies
across migration and dashboard docs. Rewrite comparison table as
3-column (.env / SecretRef / Phoenix). Lead with concepts over jargon
throughout.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@tick10
tick10 merged commit 356ad79 into main Mar 20, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant